// prefetcher.bpf.c
#include "vmlinux.h"
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>
#include "prefetcher.h"

char LICENSE[] SEC("license") = "GPL";

// Ring buffer
struct {
    __uint(type, BPF_MAP_TYPE_RINGBUF);
    __uint(max_entries, 256 * 1024);
} events SEC(".maps");

// Target PID
volatile const __u32 target_pid = 0;

// 定义 tracepoint 上下文的结构体
// 这个定义是根据 `cat /sys/kernel/debug/tracing/events/exceptions/page_fault_user/format` 的输出手动创建的
// 这是一个稳定、可靠的方式，不依赖于 vmlinux.h 是否包含特定 tracepoint 定义
struct page_fault_ctx {
    // struct trace_entry (内核通用跟踪头)
    unsigned short common_type;
    unsigned char common_flags;
    unsigned char common_preempt_count;
    int common_pid;

    // page_fault_user 的特定字段
    unsigned long address;
    unsigned long ip;
    unsigned long error_code;
};

SEC("tracepoint/exceptions/page_fault_user")
int handle_page_fault(struct page_fault_ctx *ctx) {
    __u64 pid_tgid = bpf_get_current_pid_tgid();
    __u32 pid = pid_tgid >> 32;

    if (target_pid == 0 || pid != target_pid) {
        return 0;
    }

    struct page_fault_event *e = bpf_ringbuf_reserve(&events, sizeof(*e), 0);
    if (!e) {
        return 0;
    }

    e->pid = pid;
    
    // *** 核心修正 ***
    // 使用 bpf_probe_read_kernel 从 ctx 中安全地读取 address 字段
    // 这是最可靠的方式，因为它直接从内存读取，绕过了编译器的类型检查问题
    bpf_probe_read_kernel(&e->addr, sizeof(e->addr), &ctx->address);
    
    e->timestamp = bpf_ktime_get_ns();

    bpf_ringbuf_submit(e, 0);

    return 0;
}
